package com.xinz.protocal.client;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import com.xinz.protocal.Buffer;
import com.xinz.protocal.SrmiClient;
import com.xinz.protocal.SrmiDefine;
import com.xinz.protocal.config.ConfigFactory;

/**
 * SRMI Socket连接客户端的简单实现
 * 初始化之后通过connect方法来连接服务端<br>
 * 一个SimpleSocketClient对象对应一个连接session。<br>
 * 目前方法调用是单通道的，这也意味着同一时刻只有一个方法被调用
 * 
 * @author xingyun
 *
 */
public class SimpleSocketClient extends SrmiClient{

	DataOutputStream out;
	DataInputStream in;
	Socket socket;
	Object invokeLock = new Object();
	Buffer invokeResult;
	
	boolean isStartup = false;
	Object startupLock = new Object();
	
	public SimpleSocketClient(String host, int port, ConfigFactory factory) throws UnknownHostException, IOException {
		super(factory);
		this.socket = new Socket(host, port);
	}

	@Override
	public Buffer request(Buffer request) throws IOException {
		synchronized(invokeLock){// 一次只允许一个请求存在
			// 判断是否是递归调用，FIXME:递归的时候或许有逻辑问题? zzz...
			if(Thread.currentThread() == this){
				writeBuffer(out, SrmiDefine.TYPE_REQUEST, request);
				while(receive() != SrmiDefine.TYPE_RETURN);
			}
			else {
				synchronized(out) {
					writeBuffer(out, SrmiDefine.TYPE_REQUEST, request);
				}
				try {
					invokeLock.wait();
				}
				catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		return invokeResult;
	}
	
	private Buffer readBuffer(DataInputStream in, int size) throws IOException{
		byte data[] = new byte[size];
		in.readFully(data);
		return new Buffer(data);
	}
	
	private void writeBuffer(DataOutputStream out, int type, Buffer buffer) throws IOException {
		out.writeShort(buffer.length + 3);
		out.write(type);
		buffer.writeToStream(out);
	}
	
	/***
	 * 连接到远程服务器
	 */
	public synchronized void connect() {
		super.connect();
		waitForStartup();
	}
	
	private void waitForStartup() {
		synchronized(startupLock) {
			try {
				startupLock.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	int receive() throws SocketException {
		try {
			int length = in.readShort();
			int type = in.read();
			if(type == SrmiDefine.TYPE_REQUEST || type == SrmiDefine.TYPE_REQUEST_NORETURN) {
				Buffer buffer = nativeInvoker.invoke(getSession(), readBuffer(in, length - 3));
				if(type == SrmiDefine.TYPE_REQUEST) {
					synchronized(out) {
						writeBuffer(out, SrmiDefine.TYPE_RETURN, buffer);
					}
				}
			}
			else if(type == SrmiDefine.TYPE_RETURN) {
				invokeResult = readBuffer(in, length - 3);
			}
			return type;
		}
		catch(SocketException e) {
			throw e;
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		return 0;
	}
	
	@Override
	public void run() {
		try {
			out = new DataOutputStream(socket.getOutputStream());
			in = new DataInputStream(socket.getInputStream());
			isStartup = true;
			synchronized(startupLock) {
				startupLock.notifyAll();
			}
			while(true) {
				int type = receive();
				if(type == SrmiDefine.TYPE_RETURN) {
					synchronized(invokeLock) {
						invokeLock.notifyAll();
					}
				}
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
}
